package com.cenntro.common.util;

import org.apache.commons.lang3.StringUtils;

public class BankUtil {

	public enum BankType {
		VISA(1, "VISA"), MASTERCARD(2, "MASTERCARD"), AMERICANEXPRESS(3, "AMERICANEXPRESS"), DINERSCLUB(4, "DINERSCLUB"), JCB(5, "JCB"), DISCOVER(6, "DISCOVER"), UNIONPAY(7, "UNIONPAY"), UNKNOW(0,
				"UNKNOW");
		private Integer code;
		private String name;

		private BankType(Integer code, String name) {
			this.code = code;
			this.name = name;
		}

		public static BankType getBankType(Integer code) {
			for (BankType bank : BankType.values()) {
				if (bank.code.equals(code)) {
					return bank;
				}
			}
			return null;
		}

		public Integer getCode() {
			return code;
		}

		public void setCode(Integer code) {
			this.code = code;
		}

		public String getName() {
			return name;
		}

		public void setName(String name) {
			this.name = name;
		}

	}

	/***
	 * 格式 ： 4#16#1 格式4：银行卡开头， 16 银行卡位数，1：BankType 枚举code
	 */
	private static String[] bankBin = { "4#16#1", // 4开头 16
			"5#16#2", // 万事达 5开头 13,16
			"34#15#3", "37#15#3", // 运通34 或者 37开头 15
			"6011#16#6", // DISCOVER 6011开头 16位
			"62#16#7", "62#19#7" // UNIONPAY 62开头16或者19位
	};
	/***
	 * 格式300000-305999：银行卡开头范围， 14 银行卡位数，0：BankType 枚举code
	 */
	private static String[] bankBinRange = { "300000-305999#14#4", "309500-309599#14#4", "360000-369999#14#4", "380000-399999#14#4", // 大莱卡
																																		// 300000-305999
																																		// 309500-309599
																																		// 360000-369999
																																		// 380000-399999
																																		// 14
			"352800-358999#16#5", // jcb 352800-358999 16位
	};

	// visa
	// private String masterCard[] = {"500000#13", "500000#16"};//万事达 5开头 13,16
	// private String americanExpress[] = {"340000#15", "370000#15"};//运通34 或者
	// 37开头 15
	//
	// private String dinersClub[] = {"300000-305999#14", "309500-309599#14",
	// "360000-369999#14", "380000-399999#14"};//大莱卡 300000-305999 309500-309599
	// 360000-369999 380000-399999 14
	// private String jcb[] = {"352800-358999#16"};//jcb 352800-358999 16位
	// private String discover[] = {"601100#16"}; //6011开头 16位
	// private String unionPay[] = {"620000#16", "620000#19"}; //62开头16或者19位
	//
	// {
	//
	// }

	public static BankType checkBank(String bankNum) {
		if (StringUtils.isBlank(bankNum) || bankNum.length() < 13 || bankNum.length() > 19) {
			return null;
		}

		String start = bankNum.substring(0, 6);
		for (String bin : bankBin) {
			String[] str = bin.split("#");
			if (bankNum.startsWith(str[0]) && bankNum.length() == NumUtil.toInt(str[1])) {
				return BankType.getBankType(NumUtil.toInt(str[2]));
			}
		}
		for (String bin : bankBinRange) {
			String[] str = bin.split("#");
			String range[] = str[0].split("-");

			if (range[1].compareTo(start) >= 0 && range[0].compareTo(start) <= 0) { // 不在range300000-305999这个范围
				if (bankNum.length() == NumUtil.toInt(str[1])) {
					return BankType.getBankType(NumUtil.toInt(str[2]));
				}
			}

		}
		return BankType.UNKNOW;
	}

	// 查找方法
	public static int binarySearch(int[] srcArray, int des) {
		int low = 0;
		int high = srcArray.length;
		while (low < high) {

			if (des == srcArray[low]) {
				return low;
			}
			low++;
		}
		return -1;
	}

	/*
	 * 当你输入信用卡号码的时候，有没有担心输错了而造成损失呢？其实可以不必这么担心，
	 * 因为并不是一个随便的信用卡号码都是合法的，它必须通过Luhn算法来验证通过。 该校验的过程：
	 * 1、从卡号最后一位数字开始，逆向将奇数位(1、3、5等等)相加。
	 * 2、从卡号最后一位数字开始，逆向将偶数位数字，先乘以2（如果乘积为两位数，则将其减去9），再求和。
	 * 3、将奇数位总和加上偶数位总和，结果应该可以被10整除。 例如，卡号是：5432123456788881
	 * 则奇数、偶数位（用红色标出）分布：5432123456788881 奇数位和=35 偶数位乘以2（有些要减去9）的结果：1 6 2 6 1 5 7
	 * 7，求和=35。 最后35+35=70 可以被10整除，认定校验通过。
	 * 请编写一个程序，从键盘输入卡号，然后判断是否校验通过。通过显示：“成功”，否则显示“失败”。 比如，用户输入：356827027232780
	 * 程序输出：成功
	 */

	// /**
	// * 校验银行卡卡号
	// *
	// * @param cardId
	// * @return
	// */
	// public static boolean checkBankCard(String cardId) {
	// return false;
	// }

	/**
	 * 从不含校验位的银行卡卡号采用 Luhm 校验算法获得校验位
	 * 
	 * @param nonCheckCodeCardId
	 * @return
	 */
	public static char getBankCardCheckCode(String nonCheckCodeCardId) {
		if (nonCheckCodeCardId == null || nonCheckCodeCardId.trim().length() == 0 || !nonCheckCodeCardId.matches("\\d+")) {
			// 如果传的不是数据返回N
			return 'N';
		}
		char[] chs = nonCheckCodeCardId.trim().toCharArray();
		int luhmSum = 0;
		for (int i = chs.length - 1, j = 0; i >= 0; i--, j++) {
			int k = chs[i] - '0';
			if (j % 2 == 0) {
				k *= 2;
				k = k / 10 + k % 10;
			}
			luhmSum += k;
		}
		return (luhmSum % 10 == 0) ? '0' : (char) ((10 - luhmSum % 10) + '0'); // 这边+'0'，不是拼接，在Java和C#中是8+0的ASCII码得到8在ASCII中的编码值，然后通过(char)转成字符'8'
	}

}
